package com.ruoyi.durian.service.impl;

import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.constant.DurianConstant;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.CallWithOtherType;
import com.ruoyi.common.enums.DurianRedisKeysType;
import com.ruoyi.common.enums.OrderType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.ALiYunAISDKUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.RedisGeoUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.rabbitmq.RabbitSendUtils;
import com.ruoyi.durian.domain.*;
import com.ruoyi.durian.domain.utils.CalculateTheDistanceVoUtlis;
import com.ruoyi.durian.domain.vo.CalculateTheDistanceVo;
import com.ruoyi.durian.domain.vo.SendMessageReesultVo;
import com.ruoyi.durian.mapper.*;
import com.ruoyi.durian.service.LlDriverService;
import com.ruoyi.durian.service.LlMapService;
import com.ruoyi.durian.state.impl.EndOrderStateImpl;
import com.ruoyi.durian.state.impl.WaitIngAfterOrderStateImpl;
import com.ruoyi.system.service.ISysUserService;
import org.redisson.RedissonMultiLock;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author lenovo
 * @description 针对表【ll_driver(司机表)】的数据库操作Service实现
 * @createDate 2024-07-26 16:17:15
 */
@Service
public class LlDriverServiceImpl extends ServiceImpl<LlDriverMapper, LlDriver>
        implements LlDriverService {

    @Resource
    private LlDriverMapper llDriverMapper;

    @Resource
    private RedisGeoUtils redisGeoUtils;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private LlOrderMapper llOrderMapper;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private LlMapService llMapService;

    @Resource
    private LlOrderInfoMapper llOrderInfoMapper;

    @Resource
    private LlCarMapper llCarMapper;

    @Resource
    private RabbitSendUtils rabbitSendUtils;

    @Resource
    private LlMapMapper llMapMapper;

    @Resource
    private LlUserFriendMapper llUserFriendMapper;

    @Resource
    private ISysUserService userService;

    @Resource
    private WaitIngAfterOrderStateImpl waitIngAfterOrderStateImpl;

    @Resource
    private EndOrderStateImpl endOrderStateImpl;

    @Resource
    private ALiYunAISDKUtils aLiYunAISDKUtils;


    /**
     * 向redis实时添加司机坐标
     *
     * @param message
     */
    @Override
    public void addDriver(String message) {
        Integer driverId = 1205;
        String[] map = message.split(",");
        String mapX = map[0];
        String mapY = map[1];
        Point point = new Point(Double.valueOf(mapX), Double.valueOf(mapY));
        // 司机接单状态
        String status = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.DRIVER.getKey() + driverId);

        if (status.equals("0")) {
            // 解析司机绑定订单信息
            String driverOrderHashString = stringRedisTemplate.opsForHash()
                    .get(DurianRedisKeysType.DRIVER_ORDER.getKey(), driverId.toString()).toString();

            HashMap driverOrderHash = JSONObject.parseObject(driverOrderHashString, HashMap.class);

            SendMessageReesultVo sendMessageReesultDto = new SendMessageReesultVo();
            // 乘客id 用来广播司机到达起点的时间距离
            sendMessageReesultDto.setReceiveId(Integer.valueOf(driverOrderHash.get("userId").toString()));
            // 订单id 在指定订单地图中存放司机的实时坐标
            sendMessageReesultDto.setOrderId(Integer.valueOf(driverOrderHash.get("orderId").toString()));

            String order = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey()
                    + sendMessageReesultDto.getReceiveId());
            LlOrder llOrder = JSONObject.parseObject(order, LlOrder.class);

            // 判断是否是乘客是否上车
            if (llOrder.getStatus().equals(OrderType.READY.getCode())) {
                waitIngAfterOrderStateImpl.handleDriverArrival(sendMessageReesultDto, point);
            } else if (llOrder.getStatus().equals(OrderType.IN_PROGRESS.getCode())) {
                endOrderStateImpl.handleDriverArrival(sendMessageReesultDto, point);
            }
        }

    }

    /**
     * 移除司机
     */
    @Override
    public void removeDiver() {
        Integer driverId = 1205;
//        redisGeoUtils.geoDel(DurianRedisKeysType.DRIVER.getKey(), driverId.toString());
    }

    /**
     * 初始化订单
     *
     * @param message
     */
    @Override
    public void createBeforeOrder(String message) {

        Integer userId = 1204;

        if (StringUtils.isEmpty(message)) {
            throw new ServiceException("message不能为空");
        }
        HashMap map = JSON.parseObject(message, HashMap.class);
        String mapX = map.get("mapX").toString();
        String mapY = map.get("mapY").toString();

        String order = stringRedisTemplate.opsForValue()
                .get(DurianRedisKeysType.ORDER.getKey() + userId);

        HashMap llOrder = JSONObject.parseObject(order, HashMap.class);
        String status = llOrder.get("status").toString();
        if (!status.equals("0")) {
            rabbitSendUtils.sendMessage(userId, "订单已有司机抢到，无法修改车辆类型。。。");
            return;
        }
        llOrder.put("carType", map.get("carType"));

        String reuslt = JSONObject.toJSONString(llOrder);

        stringRedisTemplate.opsForValue().set(DurianRedisKeysType.ORDER.getKey() + userId, reuslt);
        // 删除暴露订单地图
        redisGeoUtils.geoDel(DurianRedisKeysType.PERSON.getKey(), order);

        // 填充carType
        LinkedHashMap<String, Point> geomap = new LinkedHashMap<>();
        geomap.put(reuslt, new Point(Double.parseDouble(mapX), Double.parseDouble(mapY)));
        // 将乘客订单信息暴露在地图，让司机选择订单
        redisGeoUtils.geoAdd(DurianRedisKeysType.PERSON.getKey(), geomap);
    }

    /**
     * 生成前等待订单
     */
    @Override
    @Transactional
    public void saveOrder(Integer startAddressId, Integer endAddressId, Integer orderType, String startTime, Integer friendId) {

        Integer userId = 1204;

        RLock redLock = redissonClient.getLock(DurianConstant.START_ORDER_KEY);
        RedissonMultiLock lock = new RedissonMultiLock(redLock);
        lock.lock();
        try {
            String orderString = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey() + userId);
            LlOrder llOrderRedis = JSONObject.parseObject(orderString, LlOrder.class);

            // 如果乘客订单状态为0表示当前没有订单，否则代表乘客因为网络波动重连
            if (null != llOrderRedis) {
                rabbitSendUtils.sendMessage(userId, "乘客重连成功！！！");
                return;
            }

            LlOrder order = new LlOrder();
            LlMap start = llMapService.getById(startAddressId);
            LlMap end = llMapService.getById(endAddressId);
            // 封装订单
            order.setBeginName(start.getName());
            order.setBeginId(start.getId());
            order.setFinishName(end.getName());
            order.setFinishId(end.getId());
            order.setStatus(OrderType.WAITING_BEFORE.getCode());
            order.setUserId(userId);
            order.setIsDelete(0);

            // 如果是预约叫车
            if (orderType == 2) {
                order.setStartTime(DateUtils.dateTime(DateUtils.YYYY_MM_DD_HH_MM_SS, startTime));
            }
            // 封装订单详情
            LlOrderInfo orderInfo = new LlOrderInfo();
            // 帮别人叫车
            if (orderType == 3) {
                orderInfo.setFriendId(friendId);
                orderInfo.setIsMe(1);
                LlUserFriend friend = llUserFriendMapper.selectById(friendId);
                order.setUserPhone(friend.getFriendPhone().toString());
            } else {
                SysUser sysUser = userService.selectUserById(Long.valueOf(userId));
                order.setUserPhone(sysUser.getPhonenumber());
            }
            llOrderMapper.insert(order);

            orderString = JSONObject.toJSONString(order);
            stringRedisTemplate.opsForValue().set(DurianRedisKeysType.ORDER.getKey() + userId, orderString);

            orderInfo.setOrderId(order.getId());
            orderInfo.setUserId(userId);
            orderInfo.setIsMe(0);

            orderInfo.setStatus(OrderType.WAITING_BEFORE.getCode());
            orderInfo.setInvoiceStatus(1);
            orderInfo.setIsDelete(0);

            llOrderInfoMapper.insert(orderInfo);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 司机附近订单列表
     *
     * @param mapX 横坐标
     * @param mapY 纵坐标
     * @return
     */
    @Override
    public List<?> orderList(String mapX, String mapY) {
        if (StringUtils.isEmpty(mapX) || StringUtils.isEmpty(mapY)) {
            throw new ServiceException("参数错误");
        }
        return redisGeoUtils.radiusByxy(mapX, mapY, DurianRedisKeysType.PERSON.getKey(), 5, 10).getContent();
    }

    /**
     * 司机抢单
     *
     * @param orderId 订单id
     * @param userId  乘客id
     * @return
     */
    @Override
    @Transactional
    public AjaxResult robOrder(Integer orderId, Integer userId) {

        Integer driverId = 1205;

        RLock redLock = redissonClient.getLock(DurianConstant.DRIVER_ROB_ORDER + orderId);
        RedissonMultiLock lock = new RedissonMultiLock(redLock);
        lock.lock();
        try {

            String orderRedisJson = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey() + userId);
            LlOrder llOrder = JSONObject.parseObject(orderRedisJson, LlOrder.class);

            if (llOrder.getStatus() != OrderType.WAITING_BEFORE.getCode()) {
                return AjaxResult.error("订单已被抢");
            }
            llOrder.setStatus(OrderType.READY.getCode());
            // 司机
            LlDriver llDriver = llDriverMapper.selectOne(new LambdaQueryWrapper<LlDriver>()
                    .eq(LlDriver::getUserId, driverId));
            // car
            LlCar llCar = llCarMapper.selectById(llDriver.getCarId());
            List carType = JSONObject.parseObject(llOrder.getCarType().toString(), List.class);
            // 校验车辆类型
            if (carType.contains(llCar.getType())) {
                return AjaxResult.error("车辆类型不匹配");
            }

            // 修改数据库中的订单 和 订单详情
            llDriver.setStatus(0);

            // 修改司机接单状态
            llDriverMapper.updateById(llDriver);
            // 修改内存中的司机接单状态
            stringRedisTemplate.opsForValue().set(DurianRedisKeysType.DRIVER.getKey() + driverId, "0");

            // 修改订单状态
            LambdaUpdateWrapper<LlOrder> orderUpdate = new LambdaUpdateWrapper<>();
            orderUpdate.eq(LlOrder::getId, orderId);
            // 回显车的类型
            orderUpdate.set(LlOrder::getCarType, llCar.getType().toString());
            // 修改订单状态
            orderUpdate.set(LlOrder::getStatus, OrderType.READY.getCode());
            llOrderMapper.update(null, orderUpdate);

            LambdaUpdateWrapper<LlOrderInfo> orderInfoUpdate = new LambdaUpdateWrapper<>();
            orderInfoUpdate.eq(LlOrderInfo::getOrderId, orderId);
            orderInfoUpdate.set(LlOrderInfo::getDriverId, driverId);
            orderInfoUpdate.set(LlOrderInfo::getCarId, llDriver.getCarId());
            // 订单状态
            orderInfoUpdate.set(LlOrderInfo::getStatus, OrderType.READY.getCode());
            orderInfoUpdate.set(LlOrderInfo::getStartPrice, llCar.getStartPrice());
            orderInfoUpdate.set(LlOrderInfo::getUpdateTime, new Date());
            // 修改订单info 状态
            llOrderInfoMapper.update(null, orderInfoUpdate);

//            // 删除缓存中的订单数据
//            stringRedisTemplate.delete(DurianRedisKeysType.ORDER.getKey() + order.getUserId());
            // 最后删除缓存防止上面出现错误 导致缓存被删除
            // 删除地图中的订单信息 防止订单被重复抢
            redisGeoUtils.geoDel(DurianRedisKeysType.PERSON.getKey(), orderRedisJson);

            stringRedisTemplate.opsForValue().set(DurianRedisKeysType.ORDER.getKey() + userId, JSONObject.toJSONString(llOrder));

            LlMap beginAddress = llMapMapper.selectById(llOrder.getBeginId());
            LlMap finishAddress = llMapMapper.selectById(llOrder.getFinishId());
            // 初始化订单地图
            Map<String, Point> map = new LinkedHashMap<>();
            map.put("startAddress", new Point(Double.parseDouble(beginAddress.getMapX()), Double.parseDouble(beginAddress.getMapY())));
            map.put("finishAddress", new Point(Double.parseDouble(finishAddress.getMapX()), Double.parseDouble(finishAddress.getMapY())));
            redisGeoUtils.geoAdd(DurianRedisKeysType.GEO_ORDER.getKey() + orderId, map);
            // 存入内存中的司机接单hash中
            HashMap<String, Integer> hashMap = new HashMap<>();
            // userId用于广播消息
            hashMap.put("userId", userId);
            // 订单Id用于绑定司机和订单
            hashMap.put("orderId", orderId);

            String jsonString = JSONObject.toJSONString(hashMap);
            stringRedisTemplate.opsForHash().put(DurianRedisKeysType.DRIVER_ORDER.getKey(), driverId.toString(), jsonString);

            // 将消息广播到指定乘客
            rabbitSendUtils.sendMessage(userId, "有司机接单，司机正飞奔而来！！！");
            // 将抢单成功广播到司机
            rabbitSendUtils.sendMessage(driverId, "抢单成功！！！");
        } finally {
            lock.unlock();
        }
        return AjaxResult.success("抢单成功");
    }

    /**
     * 取消订单
     *
     * @param orderId
     * @return
     */
    @Override
    public AjaxResult closeOrder(Integer orderId, Integer userId) {

        Integer driverId = 1205;

        RLock redLock = redissonClient.getLock(DurianConstant.DRIVER_ROB_ORDER + orderId);
        RedissonMultiLock lock = new RedissonMultiLock(redLock);
        lock.lock();
        try {
            // 获取订单
            String orderRedis = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey() + userId);
            if (StringUtils.isEmpty(orderRedis)) {
                return AjaxResult.error("订单无法取消");
            }
            LlOrder llOrder = JSONObject.parseObject(orderRedis, LlOrder.class);
            llOrder.setStatus(OrderType.WAITING_BEFORE.getCode());
            // 更新内存中的订单状态
            orderRedis = JSONObject.toJSONString(llOrder);
            stringRedisTemplate.opsForValue().set(DurianRedisKeysType.ORDER.getKey() + userId, orderRedis);
            // 将订单数据重新暴露在地图
            Integer beginId = llOrder.getBeginId();
            LlMap begin = llMapService.getById(beginId);
            HashMap<String, Point> hashMap = new HashMap<>();
            hashMap.put(orderRedis, new Point(Double.valueOf(begin.getMapX()), Double.valueOf(begin.getMapY())));
            redisGeoUtils.geoAdd(DurianRedisKeysType.PERSON.getKey(), hashMap);
            // 修改订单状态
            LambdaUpdateWrapper<LlOrder> orderuw = new LambdaUpdateWrapper<>();
            orderuw.eq(LlOrder::getId, orderId);
            orderuw.set(LlOrder::getStatus, OrderType.WAITING_BEFORE.getCode());
            llOrderMapper.update(null, orderuw);
            // 修改订单详情状态
            LambdaUpdateWrapper<LlOrderInfo> orderInfouw = new LambdaUpdateWrapper<>();
            orderInfouw.eq(LlOrderInfo::getOrderId, orderId);
            orderInfouw.set(LlOrderInfo::getStatus, OrderType.WAITING_BEFORE.getCode());
            llOrderInfoMapper.update(null, orderInfouw);
            // 修改司机接单状态
            LambdaUpdateWrapper<LlDriver> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(LlDriver::getUserId, driverId);
            updateWrapper.set(LlDriver::getStatus, 0);
            llDriverMapper.update(null, updateWrapper);
            // 修改内存中的司机接单状态
            stringRedisTemplate.opsForValue().set(DurianRedisKeysType.DRIVER.getKey() + driverId, "1");
            // 删除订单地图
            stringRedisTemplate.delete(DurianRedisKeysType.GEO_ORDER.getKey() + orderId);
            // 将消息广播到指定乘客
            rabbitSendUtils.sendMessage(userId, "司机已经取消订单，系统为你继续寻找司机。。。");
            // 将取消订单消息广播到司机
            rabbitSendUtils.sendMessage(driverId, "取消订单成功！！！");

            return AjaxResult.success("取消订单成功");
        } finally {
            lock.unlock();
        }
    }

    /**
     * 司机创建连接将接单状态同步到redis
     *
     * @param userId
     */
    @Override
    public void createDriver(Integer userId) {
        String status = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.DRIVER.getKey() + userId);
        // 校验司机的接单状态
        if (null == status || status.equals("1")) {
            stringRedisTemplate.opsForValue().set(DurianRedisKeysType.DRIVER.getKey() + userId, "1");
        } else {
            rabbitSendUtils.sendMessage(userId, "您还有未结束的订单，请尽快处理！！！！");
        }
    }

    /**
     * 确认乘客上车
     *
     * @param orderId
     * @param phoneEnd
     * @return
     */
    @Override
    @Transactional
    public AjaxResult confirmOrder(Integer orderId, String phoneEnd) {

        Integer dirverId = 1205;

        if (StringUtils.isEmpty(phoneEnd) || phoneEnd.length() != 4) {
            return AjaxResult.error("请输入正确的手机号后四位");
        }

        RLock redLock = redissonClient.getLock(DurianConstant.DRIVER_ROB_ORDER + orderId);
        RedissonMultiLock lock = new RedissonMultiLock(redLock);
        lock.lock();
        try {
            String hasJson = stringRedisTemplate.opsForHash().get(DurianRedisKeysType.DRIVER_ORDER.getKey(), dirverId.toString()).toString();
            HashMap hashMap = JSONObject.parseObject(hasJson, HashMap.class);
            String userId = hashMap.get("userId").toString();
            String orderJson = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey() + userId);
            LlOrder llOrder = JSONObject.parseObject(orderJson, LlOrder.class);
            // 判断司机输入的乘客手机号后四位和订单绑定的手机号后四位是否一致
            if (null != llOrder
                    // 校验司机是否到达乘客位置
                    && llOrder.getStatus() == OrderType.WAITING_AFTER.getCode()
                    // 校验手机号
                    && StringUtils.isNotEmpty(llOrder.getUserPhone())
                    && llOrder.getUserPhone().endsWith(phoneEnd)) {
                // 修改缓存中的订单状态
                llOrder.setStatus(OrderType.IN_PROGRESS.getCode());
                stringRedisTemplate.opsForValue().set(DurianRedisKeysType.ORDER.getKey() + userId, JSONObject.toJSONString(llOrder));
                // 修改数据库中的订单状态
                LambdaUpdateWrapper<LlOrder> update = new LambdaUpdateWrapper<>();
                update.eq(LlOrder::getId, orderId);
                update.set(LlOrder::getStatus, OrderType.IN_PROGRESS.getCode());
                update.set(LlOrder::getStartTime, new Date());
                llOrderMapper.update(null, update);

                rabbitSendUtils.sendMessage(llOrder.getUserId(), "开始行程，请系好安全好！！！");
                rabbitSendUtils.sendMessage(dirverId, "准备出发，行车不规范，亲人两行泪！！！");
            } else {
                return AjaxResult.error("请确认到达乘客上车的指定地点并且输入正确的手机号后四位！！！！");
            }
        } finally {
            lock.unlock();
        }
        return AjaxResult.success("开始行程");
    }

    /**
     * 司机到达乘客上车位置
     *
     * @param orderId
     * @return
     */
    @Override
    @Transactional
    public AjaxResult waitlngAfterOrder(Integer orderId) {
        LlOrder taxiOrder = llOrderMapper.selectById(orderId);

        String order = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey() + taxiOrder.getUserId());
        LlOrder llOrder = JSONObject.parseObject(order, LlOrder.class);

        // 修改订单状态为等待乘客上车
        llOrder.setStatus(OrderType.WAITING_AFTER.getCode());

        stringRedisTemplate.opsForValue().set(DurianRedisKeysType.ORDER.getKey() + taxiOrder.getUserId()
                , JSONObject.toJSONString(llOrder));

        RLock redLock = redissonClient.getLock(DurianConstant.START_ORDER_KEY);
        RedissonMultiLock lock = new RedissonMultiLock(redLock);
        lock.lock();
        try {
            // 修改mysql中的订单状态
            LambdaUpdateWrapper<LlOrder> update = new LambdaUpdateWrapper<>();
            update.eq(LlOrder::getId, orderId);
            update.set(LlOrder::getStatus, OrderType.WAITING_AFTER.getCode());

            // 修改Info的订单状态
            LambdaUpdateWrapper<LlOrderInfo> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(LlOrderInfo::getOrderId, orderId);
            updateWrapper.set(LlOrderInfo::getStatus, OrderType.WAITING_AFTER.getCode());

            llOrderMapper.update(null, update);
            llOrderInfoMapper.update(null,updateWrapper);

        } finally {
            lock.unlock();
        }
        return AjaxResult.success("等待乘客上车");
    }

    /**
     * 乘客到达目的地
     *
     * @param orderId
     * @return
     */
    @Override
    @Transactional
    public AjaxResult oevrOrder(Integer orderId) {

        Integer driverId = 1205;

        LlOrder taxiOrder = llOrderMapper   .selectById(orderId);

        String order = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey() + taxiOrder.getUserId());
        LlOrder llOrder = JSONObject.parseObject(order, LlOrder.class);

        llOrder.setStatus(OrderType.TO_PAY.getCode());
        stringRedisTemplate.opsForValue().set(DurianRedisKeysType.ORDER.getKey()
                + llOrder.getUserId(), JSONObject.toJSONString(llOrder));

        RLock redLock = redissonClient.getLock(DurianConstant.START_ORDER_KEY);
        RedissonMultiLock lock = new RedissonMultiLock(redLock);
        lock.lock();
        try {
            // 全程时间
            String allTime = DateUtils.timeDistance(taxiOrder.getStartTime(), new Date());

            String midwaynum = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.MIDWAY.getKey()+orderId);

            BigDecimal allRight = new BigDecimal("0");
            for (int i = 1; i <= Integer.parseInt(midwaynum); i++) {
                String startMidwayAddress = "midwayAddress:";
                String endMidwayAddress = "midwayAddress:";
                if (i == 1){
                    startMidwayAddress = "startAddress";
                    endMidwayAddress = "midwayAddress:1";
                }
                else if (i == Integer.parseInt(midwaynum)){
                    startMidwayAddress = "midwayAddress:"+midwaynum;
                    endMidwayAddress = "finishAddress";
                }
                else {
                    startMidwayAddress = "midwayAddress:" + i;
                    endMidwayAddress = "midwayAddress:" + (i - 1);
                }
                Distance midway = redisGeoUtils.distance(DurianRedisKeysType.GEO_ORDER.getKey() + orderId
                        , startMidwayAddress, endMidwayAddress);
                CalculateTheDistanceVo calculated = CalculateTheDistanceVoUtlis.calculateTheDistance(midway);
                allRight =  allRight.add((calculated.getRang()));
            }

            // 全程价格
            BigDecimal allPirce = new BigDecimal("0");
            // 远途价格
            BigDecimal farPrice = new BigDecimal("0");
            Integer carType = Integer.parseInt(taxiOrder.getCarType().toString());

            LambdaQueryWrapper<LlCar> carQw = new LambdaQueryWrapper<>();
            carQw.eq(LlCar::getType,carType);
            LlCar llCar = llCarMapper.selectOne(carQw);

            /*
             * 起步价为五公里
             */

            if (allRight.compareTo(new BigDecimal("1")) == 1){
                BigDecimal sub = allPirce.subtract(new BigDecimal("1"));
                // 全程费用
                BigDecimal addPirce = sub.multiply(llCar.getAddPrice());
                allPirce =llCar.getStartPrice().add(addPirce);
                // 远途费
                farPrice = addPirce;
            }else {
                allPirce = llCar.getStartPrice();
            }

            // 删除订单缓存
            stringRedisTemplate.delete(DurianRedisKeysType.ORDER.getKey() + taxiOrder.getUserId());
            // 修改司机接单状态
            stringRedisTemplate.opsForValue().set(DurianRedisKeysType.DRIVER.getKey() + driverId, "1");

            // 修改mysql中的订单状态
            LambdaUpdateWrapper<LlOrder> update = new LambdaUpdateWrapper<>();
            update.eq(LlOrder::getId, orderId);
            update.set(LlOrder::getStatus, OrderType.TO_PAY.getCode());
            update.set(LlOrder::getEndTime, new Date());
            update.set(LlOrder::getPrice,allPirce);
            // 修改Info的订单状态
            LambdaUpdateWrapper<LlOrderInfo> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            lambdaUpdateWrapper.eq(LlOrderInfo::getOrderId, orderId);
            lambdaUpdateWrapper.set(LlOrderInfo::getStatus, OrderType.TO_PAY.getCode());
            lambdaUpdateWrapper.set(LlOrderInfo::getAllPrice, allPirce);
            lambdaUpdateWrapper.set(LlOrderInfo::getFarPrice, farPrice);
            lambdaUpdateWrapper.set(LlOrderInfo::getStartPrice
                    , llCar.getStartPrice() == null ? new BigDecimal("0") : llCar.getStartPrice());

            llOrderMapper.update(null, update);
            llOrderInfoMapper.update(null, lambdaUpdateWrapper);
        } finally {
            lock.unlock();
        }
        return AjaxResult.success("订单完成");
    }

    /**
     * 司机和用户聊天
     * @param message
     */
    @Override
    public void driverChat(String message) {
        Integer driverId = null;
        //判断发送的消息是否为空
        if (StringUtils.isEmpty(message)){
            throw new RuntimeException("消息不能为空");
        }
        //获取司机绑定的订单id和用于广播的userId
        String driverOrder = stringRedisTemplate.opsForHash().get(DurianRedisKeysType.DRIVER_ORDER.getKey(), driverId.toString()).toString();
        if (StringUtils.isEmpty(driverOrder)){
            rabbitSendUtils.sendMessage(driverId, "您还没有接单,不能发消息给任何用户，请等待！！！");
        }
        HashMap hashMap = JSONObject.parseObject(driverOrder, HashMap.class);
        //用于广播的userId
        int userId = Integer.parseInt(hashMap.get("userId").toString());
        //订单id
        int orderId = Integer.parseInt(hashMap.get("orderId").toString());

        //根据订单id获取订单信息
        LlOrder llOrder = llOrderMapper.selectById(orderId);
        //获取存入redis中的司机的状态
        Integer driverStatus = Integer.parseInt(stringRedisTemplate.opsForValue().get(DurianRedisKeysType.DRIVER.getKey() + driverId));
        //如果订单状态为取消订单或者订单结束或者司机状态为未接单状态则不能进行聊天
        if (llOrder.getStatus() == OrderType.END.getCode() || driverStatus == 1 || llOrder.getStatus() == OrderType.PAYMENT_COMPLETED.getCode()){
            rabbitSendUtils.sendMessage(driverId, "订单完成或者没有订单，不用再做牛马了！！！");
            return;
        }
        rabbitSendUtils.sendMessage(userId, message);

        //封装参数
        SendMessageReesultVo dto = new SendMessageReesultVo();
        dto.setSendId(driverId);
        dto.setMessage(message);
        dto.setReceiveId(userId);
        dto.setType(0);
        String string = JSONObject.toJSONString(dto);

        stringRedisTemplate.opsForList().rightPush(CallWithOtherType.DRIVER_AND_USER.getKey() + orderId, string);
        stringRedisTemplate.expire(CallWithOtherType.DRIVER_AND_USER.getKey() + orderId, 30, TimeUnit.MINUTES);
    }

    /**
     * 用户和司机聊天
     * @param message
     */
    @Override
    public void userChat(String message) {
        Integer userId=null;
        //根据userId获取订单信息
        String order = stringRedisTemplate.opsForValue().get(DurianRedisKeysType.ORDER.getKey() + userId);
        LlOrder llOrder = JSONObject.parseObject(order, LlOrder.class);

        LambdaQueryWrapper<LlOrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(LlOrderInfo::getOrderId,llOrder.getId())
                .eq(LlOrderInfo::getUserId,userId)
                .eq(LlOrderInfo::getIsDelete,0);
        LlOrderInfo orderInfo = llOrderInfoMapper.selectOne(wrapper);
        Integer driverId = orderInfo.getDriverId();

        //获取司机绑定的订单id和用于广播的userId
        String driverOrder = stringRedisTemplate.opsForHash().get(DurianRedisKeysType.DRIVER_ORDER.getKey(), driverId.toString()).toString();
        HashMap hashMap = JSONObject.parseObject(driverOrder, HashMap.class);

        if (driverId==null || llOrder==null || hashMap==null){
            throw new RuntimeException("司机没有接单或者无订单");
        }

        if (llOrder.getStatus()>=OrderType.END.getCode()){
            throw new RuntimeException("订单已经结束");
        }


        //获取存入redis中的司机的状态
        Integer driverStatus = Integer.parseInt(stringRedisTemplate.opsForValue().get(DurianRedisKeysType.DRIVER.getKey() + driverId));
        //如果订单状态为取消订单或者订单结束或者司机状态为未接单状态则不能进行聊天
        if (llOrder.getStatus() == OrderType.END.getCode() || driverStatus == 1 || llOrder.getStatus() == OrderType.PAYMENT_COMPLETED.getCode()){
            rabbitSendUtils.sendMessage(userId, "乘客您好，订单已经完成！！！");
            return;
        }

        //判断发送的消息是否为空
        if (StringUtils.isEmpty(message)){
            throw new RuntimeException("消息不能为空");
        }
        rabbitSendUtils.sendMessage(driverId, message);

        //封装参数
        //封装参数
        SendMessageReesultVo dto = new SendMessageReesultVo();
        dto.setSendId(userId);
        dto.setMessage(message);
        dto.setReceiveId(driverId);
        dto.setType(0);
        String dtos = JSONObject.toJSONString(dto);

        stringRedisTemplate.opsForList().rightPush(CallWithOtherType.DRIVER_AND_USER.getKey() + llOrder.getId(),dtos);
        stringRedisTemplate.expire(CallWithOtherType.DRIVER_AND_USER.getKey() +llOrder.getId() , 30, TimeUnit.MINUTES);
    }

    /**
     * 重连之后
     * @param userId
     */
    @Override
    public void beforeCall(Integer userId) {
        Integer driverId = 618;
        //获取司机绑定的订单id和用于广播的userId
        String driverOrder = stringRedisTemplate.opsForHash().get(DurianRedisKeysType.DRIVER_ORDER.getKey(), driverId.toString()).toString();
        if (StringUtils.isEmpty(driverOrder)){
            rabbitSendUtils.sendMessage(userId, "不能发消息！！！！");
        }
        HashMap hashMap = JSONObject.parseObject(driverOrder, HashMap.class);
        //订单id
        int orderId = Integer.parseInt(hashMap.get("orderId").toString());

        //根据订单id获取订单信息
        LlOrder llOrder = llOrderMapper.selectById(orderId);
        //如果订单状态为取消订单或者订单结束或者司机状态为未接单状态则不能进行聊天
        if (llOrder.getStatus() == OrderType.END.getCode() || llOrder.getStatus() == OrderType.PAYMENT_COMPLETED.getCode()){
            rabbitSendUtils.sendMessage(userId, "已经取消订单or订单结束");
            return;
        }
    }

    /**
     * 客服聊天
     * @param message
     * @return
     * @throws NoApiKeyException
     * @throws InputRequiredException
     */
    @Override
    public AjaxResult callWithOthers(String message) throws NoApiKeyException, InputRequiredException {
        Integer userid=619;
        if (message.isEmpty()){
            return AjaxResult.error("请输入内容");
        }
        Map<String, String> customer = aLiYunAISDKUtils.customerWithOthers(message);
        stringRedisTemplate.opsForList().rightPush(CallWithOtherType.CUSTOMER_AND_USER.getKey()+userid, message);
        stringRedisTemplate.expire(CallWithOtherType.CUSTOMER_AND_USER.getKey()+userid,30, TimeUnit.MINUTES);
        String string = customer.get("content");
        if (string.isEmpty()){
            return AjaxResult.error("小旭G了！！！！请等待客服小浩！！");
        }
        rabbitSendUtils.sendMessage(userid,string);
        System.out.println(string);
        return AjaxResult.success(string);
    }

    /**
     * 聊天界面信息展示
     * @param orderId
     * @return
     */
    @Override
    public List<?> findCallWith(Integer orderId,Integer pageNumber,Integer pageSize) {
        //求起始页码
        Integer offset= (pageNumber-1)*pageSize;
        List<String> list = stringRedisTemplate.opsForList()
                .range(DurianRedisKeysType.DRIVER_TO_USER.getKey() + orderId, offset, offset+pageSize-1);
        ArrayList<SendMessageReesultVo> dtos = new ArrayList<>();
        list.forEach(s -> {
            SendMessageReesultVo dto = JSONObject.parseObject(s, SendMessageReesultVo.class);
            dtos.add(dto);
        });

        return dtos;

    }

}